#include "MergeTool.hpp"
#include "InitCGMA.hpp"
#include "GeometryModifyTool.hpp"
#include "GeometryQueryTool.hpp"
#include "Body.hpp"
#include "CGMApp.hpp"
#include "CubitAttribManager.hpp"
#include "CubitCompat.hpp"

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>

#ifndef ENGINE
# define ENGINE 0
#endif

#define gti GeometryQueryTool::instance()
#define gmti GeometryModifyTool::instance()

#define ASSERT(A) if (!(A)) failed(#A,__FILE__,__LINE__)
void failed( const char* A, const char* FILE, int LINE )
{
  printf( "Condition failed at %s:%d : %s\n", FILE, LINE, A );
  abort();
}


static void print_usage( const char* name, std::ostream& stream )
{
  std::cout << "Usage: " << name << " <input geometry file name> #x-parts #y-parts #z-parts <output geometry file name>" << std::endl;
}


std::string type_from_file_name( const std::string& filename )
{
  size_t dot_pos = filename.find_last_of( '.' );
  if (dot_pos == std::string::npos)
    return std::string();

  std::string extension = filename.substr( dot_pos + 1 );
  std::transform( extension.begin(), extension.end(), extension.begin(), ::tolower );
  if (extension == "occ" || extension == "brep")
    return "OCC";
  else if (extension == "step" || extension == "stp")
    return "STEP";
  else if (extension == "iges" || extension == "igs")
    return "IGES";
  else if (extension == "sat")
    return "ACIS_SAT";
  else
    return std::string();
}


int main(int argc, char* argv[])
{
  if (argc !=6){
    print_usage(argv[0], std::cerr);
    exit(0);
  }
  // parse command line options for partitioning geometry for parallel mesh generation
  CubitStatus rsl = CUBIT_SUCCESS;

  rsl = InitCGMA::initialize_cgma( ENGINE );
  ASSERT(rsl);

  CGMApp::instance()->attrib_manager()->auto_flag( CUBIT_TRUE );

  std::string type = type_from_file_name( argv[1] );
  if (type.empty()) // just guess OCC
    type = "OCC";
  rsl = CubitCompat_import_solid_model(argv[1], type.c_str());
  if (rsl != CUBIT_SUCCESS) {
    PRINT_ERROR("Problems reading geometry file %s.\n", argv[1]);
    abort();
  }

  int npartsx = atoi(argv[2]), npartsy = atoi(argv[3]), npartsz = atoi(argv[4]);

  // Get the bounding box for this geometry
  DLIList<Body*> old_bodies, new_bodies, junk;
  gti->bodies(old_bodies);
  old_bodies.reset();

  std::vector<CubitBox> box;
  box.resize(old_bodies.size());
  CubitBox box1;

  for(int i=0; i< old_bodies.size (); i++){
      box[i] =old_bodies[i]->bounding_box();
      if (i>0)
        box1 = box[i-1].operator |= (box[i]);
      else
        box1 = box[i];
    }

  double bxmax =box1.max_x ();
  double bymax =box1.max_y ();
  double bzmax =box1.max_z ();

  double bxmin =box1.min_x ();
  double bymin =box1.min_y ();
  double bzmin =box1.min_z ();
  std::cout << "Bounding box of the geometry file:" << std::endl;
  std::cout << bxmin << " < X < " << bxmax << std::endl;
  std::cout << bymin << " < X < " << bymax << std::endl;
  std::cout << bzmin << " < X < " << bzmax << std::endl;
  double dx = box1.x_range ();
  double dy = box1.y_range ();
  double dz = box1.z_range ();

  for (int i=0; i< npartsx-1;  i++){
      double xcenter = bxmin + (i+1)*dx/npartsx;

      CubitVector center(xcenter,0.0, 0.0);
      CubitVector axes[2];
      axes[0].set(0.,0.,1.);
      axes[1].set(0.,1.,0.);

      new_bodies.clean_out ();
      rsl = gmti->webcut_with_planar_sheet(old_bodies,center,axes,2*dy,2*dz,new_bodies,junk);
      if (rsl== CUBIT_FAILURE)
        return rsl;
      old_bodies.clean_out ();
      old_bodies.copy_from (&new_bodies[0],new_bodies.size ());
    }

  old_bodies.clean_out ();
  gti->bodies(old_bodies);
  old_bodies.reset();
  for (int j=0; j< npartsy-1; j++){

      double ycenter = bymin + (j+1)*dy/npartsy;

      CubitVector center(0.0,ycenter, 0.0);
      CubitVector axes[2];
      axes[0].set(0.,0.,1.);
      axes[1].set(1.,0.,0.);

      new_bodies.clean_out ();
      rsl = gmti->webcut_with_planar_sheet(old_bodies,center,axes,2*dz,2*dx,new_bodies,junk);
      if (rsl== CUBIT_FAILURE)
        return rsl;
      old_bodies.clean_out ();
      old_bodies.copy_from (&new_bodies[0],new_bodies.size ());
    }

  old_bodies.clean_out ();
  gti->bodies(old_bodies);
  old_bodies.reset();
  for (int k=0; k< npartsz-1; k++){

      double zcenter = bzmin + (k+1)*dz/npartsz;

      CubitVector center(0.0,0.0, zcenter);
      CubitVector axes[2];
      axes[0].set(0.,1.,0.);
      axes[1].set(1.,0.,0.);

      new_bodies.clean_out ();
      rsl = gmti->webcut_with_planar_sheet(old_bodies,center,axes,2*dx,2*dy,new_bodies,junk);
      if (rsl== CUBIT_FAILURE)
        return rsl;
      old_bodies.clean_out ();
      old_bodies.copy_from (&new_bodies[0],new_bodies.size ());
    }

// merge entities 
  rsl = MergeTool::instance()->merge_bodies( new_bodies );
  ASSERT(rsl);

  int num_ents_exported=0;
  DLIList<RefEntity*> ref_entities;
  const CubitString cubit_version="10.2";
  const char * filename = argv[5];

  ref_entities.clean_out();
  rsl = CubitCompat_export_solid_model(ref_entities, filename, type.c_str(),
                                       num_ents_exported, cubit_version);
  if (rsl== CUBIT_FAILURE)
    return rsl;

  return 0;
}


